#ifndef _CDSLIB_GLOBALS_C
#define _CDSLIB_GLOBALS_C

#include "_cdslib_globals.h"

int
__genericCompare(const void *_objectOne, const void *_objectTwo)
{
    return (int)((long)_objectOne - (long)_objectTwo);
}

size_t
__genericHash(const void *_Object, const size_t _objectSize)
{
    char      *buffer       = NULL;
    uint512_t *chunkPointer = NULL;
    uint512_t  chunk;
    uint32_t  *w;
    uint32_t   a, b, c, d, e, f, k, temp;

    /* const unsigned char padding = 0b10000000; */
    const unsigned char padding = 0x80;
    unsigned char i;

    const size_t   objectByteLength = _objectSize;
    const uint64_t objectBitLength  = objectByteLength * 8;
    
    size_t   paddingByteLength = 1;
    uint64_t paddingBitLength  = paddingByteLength * 8;
    
    size_t   totalByteLength = objectByteLength + paddingByteLength;
    uint64_t totalBitLength  = objectBitLength  + paddingBitLength;

    size_t digest = 0x07452301EFCDAB89;

    while(((objectBitLength + paddingBitLength) - 448) % 512 != 0) {
        paddingBitLength += 8;
    }

    /* 
     * The +64 is for the byteLength of the Object (before pre-processing) 
     * as a 64-bit integer. 
     */
    totalBitLength = objectBitLength + paddingBitLength + 64; 
    totalByteLength = totalBitLength / 8;

    buffer = calloc(totalByteLength, sizeof(char));

    memcpy(buffer,                      _Object,           objectByteLength);
    memcpy(buffer + objectByteLength,   &padding,          1);
    memcpy(buffer + totalByteLength - 8, &objectBitLength, 8);
    
    for(chunkPointer = (uint512_t *)buffer; 
        chunkPointer < (uint512_t *)(buffer + totalByteLength); 
        chunkPointer++) {
        chunk = *chunkPointer;
        w = calloc(80, sizeof(uint32_t));
        memcpy(w, &chunk, sizeof(chunk));

        for(i = 16; i < 80; i++) {
            w[i] = (w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]) << 1;
        }

        a = digest & 0x00000000FFFFFFFF;
        b = (digest >> 32) & 0x00000000FFFFFFFF;
        c = a - b;
        d = b/2;
        e = a + b;

        for(i = 0; i < 80; i++) {
            if(i <= 19) {
                f = (b & c) | (~b & d);
                k = 0x5A827999;
            } else if(i <= 39) {
                f = b ^ c ^ d;
                k = 0x6ED9EBA1;
            } else if(i <= 59) {
                f = (b & c) | (b & d) | (c & d);
                k = 0x8F1BBCDC;
            } else if(i <= 79) {
                f = b ^ c ^ d;
                k = 0xCA62C1D6;
            }

            temp = (a << 5) + f + e + k + w[i];
            e = d;
            d = c;
            c = (b << 30);
            b = a;
            a = temp;
        }

        digest += a;
        digest = digest << 32;
        digest += b;
        digest = digest << 32;

        free(w);
    }
    free(buffer);

    return digest;
}

#endif
